﻿using System;
using System.IO;
using System.Net.Mime;
using System.Text;
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using Random = UnityEngine.Random;

namespace COC.Tools.Pathing2
{

    public class GridGraph : MonoBehaviour
    {


        private float rows = 20.0f;
        private float columns = 20.0f;
        private float tileSize = 10.0f;
        private float offset = .5f;
        private GameObject container;
        private GameObject indicators;

        List<CTile> allTiles = new List<CTile>();
        CTile[] BHVArray = null;
        CTile[,] tileMap = null;

        private CTile _tile;
        AIPathfinding pathfinding;
        Color color = new Color(.114f, .319f, .255f, .9f);
        Color color3 = new Color(.614f, .619f, .655f, .5f);
        Color color2 = new Color(.614f, .719f, .255f, .9f);
        Color color4 = new Color(.994f, .990f, .825f, .5f);
        Color color5 = new Color(.914f, .561f, .145f, .6f);
        Color color6 = new Color(.1014f, .971f, .255f, .5f);

        CTile[,] mObstacleMap = null;


        int targetPeopleId = 1;
        private Vector3 __targetLocalPosition = new Vector3(188.34f, characterHeightPositon, 126.78f);
        private static float characterHeightPositon = 1.5f;

        void Start()
        {
            Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");

            container = new GameObject("grid");
            tileMap = new CTile[(int)rows, (int)columns];
            mObstacleMap = new CTile[(int)rows, (int)columns];

            int index = 0;
            indicators = new GameObject("indexText");
            indicators.transform.localScale = new Vector3(.98f, characterHeightPositon, .98f);

            //generating grid
            Shader shader = Shader.Find("Transparent/Diffuse");
            for (int i = 1; i <= rows; i++)
            {
                for (int j = 1; j <= columns; j++)
                {
                    GameObject item = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    item.name = "item_" + (i - 1) + "_" + (j - 1);
                    item.transform.localPosition = new Vector3(i * (tileSize + offset), 1.0f, j * (tileSize + offset));
                    item.transform.localScale = new Vector3(tileSize, 1.0f, tileSize);
                    item.transform.parent = container.transform;
                    item.renderer.material.color = Color.Lerp(color, color2, 3.0f * Time.deltaTime);
                    item.renderer.material.shader = shader;
                    _tile = new CTile(item.transform.localPosition, item.transform);
                    _tile.index = index++;
                    _tile.g_value = i + j;
                    _tile.pointX = i;
                    _tile.pointY = j;
                    //show index
                    GameObject indexText = new GameObject();
                    indexText.name = "item" + i + "_" + j + "_index";
                    indexText.transform.parent = indicators.transform;
                    indexText.transform.localPosition = new Vector3(i * (tileSize + offset), 1.2f, j * (tileSize + offset) + tileSize / 2);
                    indexText.transform.localRotation = Quaternion.AngleAxis(90, Vector3.right);
                    indexText.transform.localScale = new Vector3(tileSize, tileSize, tileSize);
                    indexText.AddComponent<MeshRenderer>();
                    indexText.renderer.material = ArialFont.material;
                    TextMesh tm = indexText.AddComponent<TextMesh>();
                    tm.text = "[" + (i - 1) + "," + (j - 1) + "]";
                    tm.characterSize = 0.25f;
                    tm.font = ArialFont;
                    tm.color = Color.red;
                    mObstacleMap[i - 1, j - 1] = new CTile();
                    mObstacleMap[i - 1, j - 1].walkable = 0;
                    tileMap[i - 1, j - 1] = _tile;
                }
            }
            for (int i = 0; i < tileMap.GetLength(0); i++)
            {
                for (int j = 0; j < tileMap.GetLength(1); j++)
                {
                    allTiles.Add(tileMap[i, j]);
                }
            }
            BHVArray = allTiles.ToArray();
            CTile temp = null;
            int middle = 1;
            int numberOfOpenListItems = 0;
            int p;
            for (p = 1; p <= BHVArray.Length; p++)
            {
                if (p * 2 + 1 < BHVArray.Length - 1 && Math.Abs(p % middle) == 0)
                {

                    if (BHVArray[p * 2 - 1].g_value > BHVArray[p * 2].g_value)
                    {
                        temp = BHVArray[p * 2 - 1];
                        BHVArray[p * 2 - 1] = BHVArray[p * 2];
                        BHVArray[p * 2] = temp;

                    }
                    if (BHVArray[p * 2].g_value > BHVArray[p * 2 + 1].g_value)
                    {
                        temp = BHVArray[p * 2 + 1];
                        BHVArray[p * 2 + 1] = BHVArray[p * 2];
                        BHVArray[p * 2] = temp;
                    }
                    middle *= 2;
                }


            }

            //  for (int k = 0; k < BHVArray.Length; k++)
            // {
            //   UnityEngine.Debug.Log("index[" + BHVArray[k].index + "] g value=" + BHVArray[k].g_value);
            // }
            //set some obstacle
            GameObject obstacleObj = null;
            Vector3 obstacleTilePos = Vector3.zero;
            GameObject obstacles = new GameObject();
            obstacles.name = "Obstacles";
            obstacles.transform.localScale = new Vector3(obstacles.transform.localScale.x, .97f, obstacles.transform.localScale.z);
            //obstacles.transform.parent = container.transform;
            p = 0;
            int k = 0;
            int targetRow = (int)(__targetLocalPosition.x / (tileSize + offset));
            int targetCol = (int)(__targetLocalPosition.z / (tileSize + offset));

            mObstacleMap = calculateObstacleLayer(obstacles, mObstacleMap, targetRow, targetCol);

            pathfinding = this.gameObject.AddComponent<AIPathfinding>();

            pathfinding.mBHVArray = BHVArray;
            pathfinding.mOffset = offset;
            pathfinding.mTileSize = tileSize;
            pathfinding.mAnsestor = container.transform;
            pathfinding.mRows = rows;
            pathfinding.mColumns = columns;
            pathfinding.mHeight = characterHeightPositon;
            pathfinding.mObstacleMap = mObstacleMap;

            //follower
            GameObject onePerson = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            MovePath movePath = onePerson.AddComponent<MovePath>();
            movePath.findingPath = pathfinding;
            movePath.id = targetPeopleId;
            movePath.mTileSize = tileSize;
            movePath.mOffset = offset;
            onePerson.name = "follower";
            onePerson.transform.localPosition = new Vector3(29.34f, characterHeightPositon, 16.78f);
            onePerson.transform.localScale = new Vector3(tileSize / 2, .11f, tileSize / 2);
            onePerson.renderer.material.color = new Color(.150f, .606f, .1209f, .75f);
            int currRow = (int)(onePerson.transform.localPosition.x / (tileSize + offset)) + 1;
            int currCol = (int)(onePerson.transform.localPosition.z / (tileSize + offset)) + 1;
            movePath.startingX = Mathf.Round((currRow * (tileSize + offset)));
            movePath.startingZ = Mathf.Round((currCol * (tileSize + offset)));
            movePath.speed = 1.0f;
            //target person
            GameObject anotherPerson = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            anotherPerson.name = "targetPerson";

            anotherPerson.transform.localPosition = __targetLocalPosition;
            anotherPerson.transform.localScale = new Vector3(tileSize / 2, .11f, tileSize / 2);
            anotherPerson.renderer.material.color = new Color(.910f, .366f, .209f, .5f);

            StartCoroutine(pathfinding.FindPath(targetPeopleId, onePerson.transform, anotherPerson.transform));


        }



        private CTile[,] calculateObstacleLayer(GameObject obstacles, CTile[,] obstacleMap, int targetRow, int targetCol)
        {
            generateObstacle(obstacles, 2, 3, 1, 9, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 10, 9, 11, 8, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 6, 1, 10, 5, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 11, 3, 2, 4, obstacleMap, targetRow, targetCol);
            generateObstacle(obstacles, 11, 10, 2, 2, obstacleMap, targetRow, targetCol);
            StringBuilder strBuilder = new StringBuilder();
            int count = 0;
            strBuilder.Append("this is unwalkable positon: { ");
            for (int i = 0; i < obstacleMap.GetLength(0); i++)
            {
                for (int j = 0; j < obstacleMap.GetLength(1); j++)
                {
                    if (obstacleMap[i, j].walkable == 1)
                    {
                        strBuilder.Append("obstacleMap[" + i + "," + j + "],");
                        count++;
                    }
                }
            }
            string unwalkableInfos = strBuilder.ToString().Substring(0, strBuilder.Length - 1) + " }.";

            UnityEngine.Debug.Log("counts: " + count + "," + unwalkableInfos);
            return obstacleMap;
        }

        private void generateObstacle(GameObject obstacles, int startXIndex, int startYIndex, int rowRangeScope, int colRangeScope, CTile[,] targeObstacleMap, int targetRow, int targetCol)
        {

            int p;
            int k;
            Vector3 obstacleTilePos;
            GameObject obstacleObj;
            for (p = startXIndex; p < rows - rowRangeScope - 1; p++)
            {
                var range = (int)Random.Range(1, rows - 4);
                int divisor = (range < 2) ? 2 : range;
                for (k = startYIndex; k < columns - colRangeScope - 1; k++)
                {
                    if (p < rows && k < columns && p != targetRow && k != targetCol)
                    {
                        if (targeObstacleMap[p - 1, k - 1].walkable != 1)
                        {

                            if (p % divisor == 1 || p % (divisor - 1) == 0)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleD_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color4, color6, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = 1;
                                k++;
                                continue;

                            }
                            if (p % divisor == 0 || p % (divisor - 1) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleA_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color3, color6, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = 1;
                                p++;
                                continue;


                            }
                            if (k % (divisor) == 0 || k % (divisor - 1) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleB_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color5, color2, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = 1;
                                k++;

                                continue;

                            }
                            if (k % (divisor + 1) == 0 || k % (divisor) == 1)
                            {
                                obstacleTilePos = new Vector3(p * (tileSize + offset) - (offset + .05f), 1.6f, k * (tileSize + offset) - (offset + .05f));
                                obstacleObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                                obstacleObj.transform.parent = obstacles.transform;
                                obstacleObj.transform.localPosition = obstacleTilePos;
                                obstacleObj.transform.localScale = new Vector3(tileSize - .5f, .03f, tileSize - .5f);
                                obstacleObj.name = "obstacleC_" + (p - 1) + "_" + (k - 1);
                                obstacleObj.renderer.material.color = Color.Lerp(color6, color2, 3.0f * Time.deltaTime);
                                targeObstacleMap[p - 1, k - 1].walkable = 1;
                                continue;

                            }
                        }

                    }
                }
            }

        }

        void Update()
        {

        }

        void OnGUI()
        {
            GUI.backgroundColor = Color.Lerp(color, color2, 3.0f * Time.deltaTime);
            GUILayout.BeginArea(new Rect(.0f, Screen.height - 120.0f, 130.0f, 140.0f));
            GUI.Box(new Rect(.0f, .0f, 100.0f, 140.0f), "Menu");
            if (GUI.Button(new Rect(.0f, 30.0f, 100.0f, 20.0f), "Move"))
            {

            }
            GUI.Button(new Rect(.0f, 60.0f, 100.0f, 20.0f), "Attack");
            GUI.Button(new Rect(.0f, 90.0f, 100.0f, 20.0f), "End Turn");
            GUILayout.EndArea();
        }

    }
}